import config from '@/config'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { toast, showConfirm, tansParams } from '@/utils/common'

let timeout = 10000
const baseUrl = config.baseUrl

const upload = config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  config.header = config.header || {}
  if (getToken() && !isToken) {
    config.header['Authorization'] = 'Bearer ' + getToken()
  }
  // get请求映射params参数
  if (config.params) {
    let url = config.url + '?' + tansParams(config.params)
    url = url.slice(0, -1)
    config.url = url
  }
  return new Promise((resolve, reject) => {
      uni.uploadFile({
        timeout: config.timeout || timeout,
        url: baseUrl + config.url,
        filePath: config.filePath,
        name: config.name || 'file',
        header: config.header,
        formData: config.formData,
        success: (res) => {
          let result = JSON.parse(res.data)
          const code = result.code || 200
          const msg = errorCode[code] || result.msg || errorCode['default']
          if (code === 200) {
            resolve(result)
          } else if (code == 401) {
            showConfirm("登录状态已过期，您可以继续留在该页面，或者重新登录?").then(res => {
              if (res.confirm) {
                uni.removeStorageSync('token');
                uni.reLaunch({ url: '/pages/login/login' });
              }
            })
            reject('无效的会话，或者会话已过期，请重新登录。')
          } else if (code === 500) {
            toast(msg)
            reject('500')
          } else if (code !== 200) {
            toast(msg)
            reject(code)
          }
        },
        fail: (error) => {
          let { message } = error
          if (message == 'Network Error') {
            message = '后端接口连接异常'
          } else if (message.includes('timeout')) {
            message = '系统接口请求超时'
          } else if (message.includes('Request failed with status code')) {
            message = '系统接口' + message.substr(message.length - 3) + '异常'
          }
          toast(message)
          reject(error)
        }
      })
  })
}

class UploadManager {
  constructor() {
    this.uploadTasks = new Map() // 存储上传任务
    this.chunkSize = 1024 * 1024 * 2 // 分片大小，2MB
    this.maxConcurrent = 3 // 最大并发数
  }

  /**
   * 开始上传文件
   * @param {Object} options 上传配置
   * @param {String} options.filePath 文件路径
   * @param {Function} options.onProgress 进度回调
   * @param {Function} options.onSuccess 成功回调
   * @param {Function} options.onError 错误回调
   */
  async startUpload(options) {
    try {
      const { filePath } = options
      
      // 获取文件信息
      const fileInfo = await this.getFileInfo(filePath)
      const fileSize = fileInfo.size
      
      // 计算分片数量
      const totalChunks = Math.ceil(fileSize / this.chunkSize)
      
      // 生成文件唯一标识
      const fileId = await this.generateFileId(filePath)
      
      // 获取已上传的分片信息
      const uploadedChunks = await this.getUploadedChunks(fileId)
      
      // 创建上传任务
      const task = {
        fileId,
        filePath,
        fileSize,
        totalChunks,
        uploadedChunks,
        options,
        status: 'uploading'
      }
      
      this.uploadTasks.set(fileId, task)
      
      // 开始上传分片
      await this.uploadChunks(task)
      
      // 上传完成，合并分片
      if (task.status === 'uploading') {
        const mergeResult = await this.mergeChunks(task)
        if (mergeResult.code === 200) {
          task.status = 'completed'
          options.onSuccess && options.onSuccess(mergeResult.data)
          return mergeResult.data
        } else {
          throw new Error(mergeResult.msg || '合并文件失败')
        }
      }
    } catch (error) {
      console.error('上传失败:', error)
      options.onError && options.onError(error)
      throw error
    }
  }

  /**
   * 暂停上传
   * @param {String} fileId 文件ID
   */
  pauseUpload(fileId) {
    const task = this.uploadTasks.get(fileId)
    if (task) {
      task.status = 'paused'
    }
  }

  /**
   * 恢复上传
   * @param {String} fileId 文件ID
   */
  async resumeUpload(fileId) {
    const task = this.uploadTasks.get(fileId)
    if (task && task.status === 'paused') {
      task.status = 'uploading'
      await this.uploadChunks(task)
      
      // 如果所有分片都上传完成，执行合并
      if (task.status === 'uploading') {
        await this.mergeChunks(task)
        task.status = 'completed'
        task.options.onSuccess && task.options.onSuccess()
      }
    }
  }

  /**
   * 取消上传
   * @param {String} fileId 文件ID
   */
  cancelUpload(fileId) {
    const task = this.uploadTasks.get(fileId)
    if (task) {
      task.status = 'cancelled'
      this.uploadTasks.delete(fileId)
    }
  }

  /**
   * 获取文件信息
   * @param {String} filePath 文件路径
   */
  getFileInfo(filePath) {
    return new Promise((resolve, reject) => {
      uni.getFileInfo({
        filePath,
        success: resolve,
        fail: reject
      })
    })
  }

  /**
   * 生成文件唯一标识
   * @param {String} filePath 文件路径
   */
  async generateFileId(filePath) {
    // 这里使用文件的大小和最后修改时间作为唯一标识
    const fileInfo = await this.getFileInfo(filePath)
    return `${fileInfo.size}_${Date.now()}`
  }

  /**
   * 获取已上传的分片信息
   * @param {String} fileId 文件ID
   */
  async getUploadedChunks(fileId) {
    try {
      const token = getToken()
      const response = await uni.request({
        url: `${config.baseUrl}/system/file/chunks/${fileId}`,
        method: 'GET',
        header: {
          'Authorization': 'Bearer ' + token
        }
      })
      
      if (response.statusCode === 200) {
        return response.data.chunks || []
      }
      return []
    } catch (error) {
      console.error('获取分片信息失败:', error)
      return []
    }
  }

  /**
   * 上传分片
   * @param {Object} task 上传任务
   */
  async uploadChunks(task) {
    const chunks = []
    for (let i = 0; i < task.totalChunks; i++) {
      if (!task.uploadedChunks.includes(i)) {
        chunks.push(i)
      }
    }

    // 并发上传分片
    const uploadPromises = []
    for (let i = 0; i < chunks.length && task.status === 'uploading'; i++) {
      const chunkIndex = chunks[i]
      const start = chunkIndex * this.chunkSize
      const end = Math.min(start + this.chunkSize, task.fileSize)
      
      if (uploadPromises.length >= this.maxConcurrent) {
        await Promise.race(uploadPromises)
        uploadPromises.splice(0, uploadPromises.length)
      }
      
      uploadPromises.push(this.uploadChunk(task, chunkIndex, start, end))
      
      // 更新进度
      const progress = ((task.uploadedChunks.length + uploadPromises.length) / task.totalChunks) * 100
      task.options.onProgress && task.options.onProgress(progress)
    }
    
    await Promise.all(uploadPromises)
  }

  /**
   * 上传单个分片
   * @param {Object} task 上传任务
   * @param {Number} chunkIndex 分片索引
   * @param {Number} start 起始位置
   * @param {Number} end 结束位置
   */
  uploadChunk(task, chunkIndex, start, end) {
    return new Promise((resolve, reject) => {
      const { fileId, filePath } = task
      const token = getToken()
      
      uni.uploadFile({
        url: `${config.baseUrl}/system/file/chunk`,
        filePath,
        name: 'file',
        formData: {
          fileId,
          chunkIndex,
          start,
          end,
          total: task.totalChunks
        },
        header: {
          'Authorization': 'Bearer ' + token
        },
        success: (res) => {
          if (res.statusCode === 200) {
            const result = JSON.parse(res.data)
            if (result.code === 200) {
              task.uploadedChunks.push(chunkIndex)
              resolve()
            } else {
              reject(new Error(result.msg || '上传分片失败'))
            }
          } else {
            reject(new Error('上传分片失败'))
          }
        },
        fail: reject
      })
    })
  }

  /**
   * 合并分片
   * @param {Object} task 上传任务
   */
  async mergeChunks(task) {
    const { fileId } = task
    const token = getToken()
    
    const response = await uni.request({
      url: `${config.baseUrl}/system/file/merge/${fileId}`,
      method: 'POST',
      header: {
        'Authorization': 'Bearer ' + token
      }
    })
    
    if (response.statusCode !== 200) {
      throw new Error('合并分片失败')
    }
    
    return response.data
  }
}

// 创建并导出单例实例
const uploadManagerInstance = new UploadManager()
export default uploadManagerInstance
